import pdb
import sys
import types
import urllib2
import time
import datetime

class SimpleLogger:
    FATAL = 1
    ERROR = 2
    WARNING = 3
    INFO = 4
    DEBUG = 5

    levels = \
    {
     FATAL     : 'FATAL',
     ERROR     : 'ERROR',
     WARNING   : 'WARNING',
     INFO      : 'INFO',
     DEBUG     : 'DEBUG'
    }
    DATEFORMAT = '%Y-%m-%dT%H:%M:%S'

    def __init__(self):
        self.flog   = None
        self.fpath  = None
        self.level  = SimpleLogger.INFO
        self.ident  = ''

    @staticmethod
    def formatmsg(level, msg):
            #time stamp
        stamp = datetime.datetime.now().strftime(SimpleLogger.DATEFORMAT)
        return '%s %s %s' % (stamp, str(level), str(msg))

    def setfile(self, path):
        try:
            self.flog = open(path, 'w')
            self.fpath = path
        except Exception,err:
            msg = formatmsg(SimpleLogger.FATAL, 'setfile: ' + str(err))
            print msg

    def setlevel(self, level):
        if level in SimpleLogger.levels.keys():
            self.level = level
        else:
            raise RuntimeError('SimpleLogger::formatmsg: unknown level(%s)' % str(level))

    def setident(self, ident):
        if types.StringType != type(ident): raise RuntimeError('SimpleLogger::setident: ident should be a string')
        self.ident = ident

    def log(self, level, msg):
        if self.level >= level:
            fullmsg = self.formatmsg(self.levels[level], self.ident + ' ' + msg)
            print fullmsg
            if self.flog != None:
                self.flog.write(fullmsg + '\n')
                self.flog.flush()

    def error(self, msg): self.log(SimpleLogger.ERROR, msg)
    def warning(self, msg): self.log(SimpleLogger.WARNING, msg)
    def info(self, msg): self.log(SimpleLogger.INFO, msg)
    def debug(self, msg): self.log(SimpleLogger.DEBUG, msg)

class Logs:
    ROOT = "root"
    OBJECTS = "objects"
    MSAPI = "msapi"
    CREDODB = "credodb"

    def __init__(self):
        self.logs = {}
    def at(self, key):
        if key not in self.logs:
            self.logs[key] = SimpleLogger()
        return self.logs[key]
    def setRootFile(self, fpath = None):
        if fpath == None:
            fpath = sys.argv[0] + '.log'
        self.at(Logs.ROOT).setfile(fpath)

    def gerror(self, msg): self.at(Logs.ROOT).error(msg)
    def gwarning(self, msg): self.at(Logs.ROOT).warning(msg)
    def ginfo(self, msg): self.at(Logs.ROOT).info(msg)
    def gdebuf(self, msg): self.at(Logs.ROOT).debug(msg)

    def fatal(self, key, msg):
        if key in self.logs:
            self.logs[key].fatal(msg)
        else:
            self.at(Logs.ROOT).fatal(msg)

    def error(self, key, msg):
        if key in self.logs:
            self.logs[key].error(msg)
        else:
            self.gerror(msg)

    def warning(self, key, msg):
        if key in self.logs:
            self.logs[key].warning(msg)
    def info(self, key, msg):
        if key in self.logs:
            self.logs[key].info(msg)
    def debug(self, key, msg):
        if key in self.logs:
            self.logs[key].debug(msg)


glogger = Logs()

class Object:
    def __init__(self):
        self.msid = 0
        self.targetid = ''
        self.skey = ''
        self.name = ''

        self.caronline_login = ''
        self.caronline_password = ''

        self.events = {}

    def __cmp__(x, y):
        return x.targetid < y.targetid

class Objects:
    @staticmethod
    def fromFile(objs, path):
        '''
        file format:
        id=<targetid> name=<name> skey=<skey> ...
        [id=<targetid> name=<name> skey=<skey> ...]
        '''
        try:
            f = open(path, 'r')
            lines = f.readlines()
            glogger.debug(Logs.OBJECTS, 'fromFile: path(%s) lines(%d)' % (path, len(lines)))
        except Exception,err:
            glogger.error(Logs.OBJECTS, 'fromFile: %s' % str(err))

        for line in lines:
            try:
                parsed = {}
                buf = line.split(' ')
                for prop in buf:
                    prop = prop.split('=')
                    parsed[prop[0]] = prop[1]

                obj = Object()
                obj.targetid = parsed['id']
                if parsed.has_key('skey'): obj.skey = parsed['skey']
                if parsed.has_key('login'): obj.caronline_login = parsed['login']
                if parsed.has_key('password'): obj.caronline_password = parsed['password']

                objs[obj.targetid] = obj
                glogger.debug(Logs.OBJECTS, 'fromFile: id(%s) as %s' % (obj.targetid, str(parsed)))

            except Exception,err:
                glogger.warning(Logs.OBJECTS, 'fromFile: line(%s): %s' % (str(line), str(err)))

        glogger.info(Logs.OBJECTS, 'fromFile: path(%s) lines(%d) objects(%d)' % (path, len(lines), len(objs)))

class CarOnlineAPI:
    def __init__(self, eventTypes, url = 'http://dev.car-online.ru/'):
        self.url = url
        self.eventTypes = eventTypes

    @staticmethod
    def mergeevents(x, y):
        for k, v in y.iteritems():
            if x.has_key(k):
                if k == 'type' and v == '89' and x[k] != '89':
                    continue
                elif k == 'contactIdCode' and v == 'Z60' and x[k] != 'Z60':
                    continue
                x[k] = v ###TODO: or realy merge it??? 
            else:
                x[k] = v

    def forceInit(self, objects):
        for o in objects.values():
            try:
                if len(o.skey) > 0:
                    glogger.debug(Logs.MSAPI, 'forceInit: id(%s) has skey(%s)' % (o.targetid, o.skey))
                    continue
                url = self.url + '/api/v2?get=securityKey&login=%s&password=%s' % (o.caronline_login, o.caronline_password)
                glogger.debug(Logs.MSAPI, 'forceInit: id(%s): %s' % (o.targetid, url))

                for l in urllib2.urlopen(url).readlines():
                    buff = l.replace('</skey>', '').split('<skey>')
                    for skey in buff[1:]:
                        o.skey = skey

                glogger.info(Logs.MSAPI, 'forceInit: id(%s) has skey(%s) <- %s/%s' % (o.targetid, o.skey, o.caronline_login, o.caronline_password))
            except Exception, err:
                glogger.error(Logs.MSAPI, 'forceInit: id(%s): %s' % (o.targetid, str(err)))

    def getLowerBound(self):
        today = datetime.datetime.today()
        yesterday = today - datetime.timedelta(days=1)
        firstday = None
        if yesterday.day > 1:
            firstday = datetime.datetime(year=yesterday.year, month=yesterday.month, day=yesterday.day)
        else:
            firstday = datetime.datetime(year=yesterday.year, month=yesterday.month, day=1)
        firstdaystr = str(int(time.mktime(firstday.timetuple()))) + '000'
        return (firstday, firstdaystr)


    def getPGSMEvents(self, processed, processing, objects):
        firstday, firstdaystr = self.getLowerBound()
        totalloaded = 0
        for o in objects.values():
            try:
                loaded = 0
                if len(o.skey) == 0:
                    raise RuntimeError('CarOnlineAPI::getPGSMEvents: id(%s) has no skey' % (o.targetid))
                url = self.url + 'api/v2?get=events&skey=%s&begin=%s' % (o.skey, firstdaystr)

                docs = urllib2.urlopen(url).read()
                for l in docs.split('<event ')[1:]:
                    try:
                        l = l.replace('</eventlist>', '').replace('/>', '').replace('"', '')
                        event = {}

                        for part in l.split():
                            part = part.split('=')
                            if len(part[0]) > 0 and len(part[1]) > 0:
                                if part[0] == 'eventTime':
                                    event[part[0]] = datetime.datetime.fromtimestamp(int(part[1][:10]))
                                else:
                                    event[part[0]] = part[1]

                        event['objectId'] = o.targetid
                        eventid = int(event['eventId'])
                        eventtype = int(event['type'])

                        if eventtype not in self.eventTypes: continue
                        event['contactIdCode'] = str(self.eventTypes[eventtype])
                        if eventtype == 20 or eventtype == 54: event['credoZoneCode'] = '1'
                        elif eventtype == 21 or eventtype == 55: event['credoZoneCode'] = '2'
                        elif eventtype == 22 or eventtype == 56: event['credoZoneCode'] = '3'
                        else: event['credoZoneCode'] = '0'

                        if 'e_%d' % eventid not in processed:
                            if event['eventTime'] - firstday > datetime.timedelta(seconds=1):
                                if o.events.has_key(eventid):
                                    CarOnlineAPI.mergeevents(o.events[eventid], event)
                                else:
                                    o.events[eventid] = event
                                loaded += 1
                                totalloaded += 1
                            processing.add('e_%d' % eventid)

                    except Exception, err:
                        glogger.warning(Logs.MSAPI, 'getPGSMEvents: id(%s) line(%s): %s' % (o.targetid, str(l), str(err)))
                        continue
                glogger.debug(Logs.MSAPI, 'getPGSMEvents: id(%s) events(%d/%d): %s' % (o.targetid, loaded, len(o.events), url))

            except Exception, err:
                glogger.error(Logs.MSAPI, 'getPGSMEvents: id(%s): %s' % (o.targetid, str(err)))
                continue
        glogger.info(Logs.MSAPI, 'getPGSMEvents: %d received' % totalloaded)
    def getContactIdEvents(self, processed, processing, objects):
        firstday, firstdaystr = self.getLowerBound()
        totalloaded = 0
        for o in objects.values():
            try:
                loaded = 0
                if len(o.skey) == 0:
                    raise RuntimeError('CarOnlineAPI::getContactIdEvents: id(%s) has no skey' % (o.targetid))
                url = self.url + 'api/v2?get=contactId&skey=%s&begin=%s' % (o.skey, firstdaystr)

                docs = urllib2.urlopen(url).read()
                #for l in docs: pass

                glogger.debug(Logs.MSAPI, 'getContactIdEvents: id(%s) events(%d/%d): %s' % (o.targetid, loaded, len(o.events), url))

            except Exception, err:
                glogger.error(Logs.MSAPI, 'getContactIdEvents: id(%s): %s' % (o.targetid, str(err)))
                continue
        glogger.info(Logs.MSAPI, 'getContactIdEvents: %d received' % totalloaded)

    def getPhotos(self, processed, processing, objects):
        firstday, firstdaystr = self.getLowerBound()
        totalphotosloaded = 0
        for o in objects.values():
            try:
                loaded = 0
                if len(o.skey) == 0:
                    raise RuntimeError('CarOnlineAPI::getPhotos: id(%s) has no skey' % (o.targetid))
                url = self.url + 'api/v2?get=photos&skey=%s&begin=%s' % (o.skey, firstdaystr)

                docs = urllib2.urlopen(url).read()
                for l in docs.split('<photo ')[1:]:
                    try:
                        l = l.replace('</photolist>', '').replace('/>', '').replace('"', '')
                        photo = {}

                        for part in l.split():
                            part = part.split('=')
                            if len(part[0]) > 0 and len(part[1]) > 0:
                                if part[0] == 'eventTime':
                                    photo[part[0]] = datetime.datetime.fromtimestamp(int(part[1][:10]))
                                else:
                                    photo[part[0]] = part[1]

                        photo['skey'] = o.skey
                        photo['objectId'] = o.targetid
                        photo['isPhoto'] = True
                        photo['photoId'] = photo['id']
                        eventid = int(photo['eventId'])
                        photo['contactIdCode'] = self.eventTypes[89]
                        photo['credoZoneCode'] = '4'

                        if 'f_%d' % eventid not in processed:
                            if photo['eventTime'] - firstday > datetime.timedelta(seconds=1):
                                if o.events.has_key(eventid):
                                    CarOnlineAPI.mergeevents(o.events[eventid], photo)
                                else:
                                    o.events[eventid] = photo
                                loaded += 1
                                totalphotosloaded += 1
                            processing.add('f_%d' % eventid)

                    except Exception, err:
                        glogger.warning(Logs.MSAPI, 'getPhotos: id(%s) line(%s): %s' % (o.targetid, str(l), str(err)))
                        continue
                glogger.debug(Logs.MSAPI, 'getPhotos: id(%s) photos(%d/%d): %s' % (o.targetid, loaded, len(o.events), url))


            except Exception, err:
                glogger.error(Logs.MSAPI, 'getPhotos: id(%s): %s' % (o.targetid, str(err)))
                continue
        glogger.info(Logs.MSAPI, 'getPhotos: %d received' % totalphotosloaded)

try:
    import pymssql
except:
    glogger.gerror('pymssql is not imported!')

class CredoDB:
    def __init__(self, params):
        self.params = params
    def connection(self):
        self.params['database'] = 'pult4db'
        pult4db = pymssql.connect(**self.params)
        glogger.debug(Logs.CREDODB, 'connected to "%s"' % str(self.params))

        self.params['database'] = 'pult4db_archives'
        pult4db_archives = pymssql.connect(**self.params)
        glogger.debug(Logs.CREDODB, 'connected to "%s"' % str(self.params))

        self.params['database'] = 'pult4db_videoarchives'
        pult4db_videoarchives = pymssql.connect(**self.params)
        glogger.debug(Logs.CREDODB, 'connected to "%s"' % str(self.params))

        return (pult4db, pult4db_archives, pult4db_videoarchives)

    def writeEvent(self, event):
        connection = self.connection()
        cursor0 = connection[0].cursor()
        cursor1 = connection[1].cursor()
        cursor2 = connection[2].cursor()

        dbtable1 = 'archive' + datetime.datetime.today().strftime('%Y%m') + '01'
        getEventId1 = 'SELECT MAX(event_id) FROM %s' % dbtable1
        cursor1.execute(getEventId1)

        getEventId0 = 'SELECT MAX(event_id) FROM temp'
        cursor0.execute(getEventId0)

        eventId = str(max(int(cursor1.fetchall()[0][0]) + 1, int(cursor0.fetchall()[0][0] + 1)))

        if event.has_key('type') and event['type'] != '24' and event['type'] != '25' and event['type'] != '26':
            #putTemp =  "INSERT INTO temp (event_id, panel_id, group_, line, zone, code, codegroup, timeevent, receiver, phone, metercount,stateevent,event_parent_id,date_key,priority,computer,bitmask) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" % (eventId, event['objectId'], '1', 'V201', '2', str(event['contactIdCode']), '1', event['eventTime'].strftime('%Y-%m-%d %H:%M:%S') + '.000', '0', 'NULL', 'NULL', '0', eventId, event['eventTime'].strftime('%Y%m%d'), '2', 'NULL', '0')
            if event.has_key('isPhoto') and event['isPhoto'] == True:
                putTemp =  "INSERT INTO temp (event_id, panel_id, group_, line, zone, code, codegroup, timeevent,stateevent,date_key,priority,bitmask,metercount) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" % (eventId, event['objectId'], '1', 'V201', str(event['credoZoneCode']), str(event['contactIdCode']), '1', event['eventTime'].strftime('%Y-%m-%d %H:%M:%S') + '.000', '0', event['eventTime'].strftime('%Y%m%d'), '2', '0', 'camera1')
            else:
                putTemp =  "INSERT INTO temp (event_id, panel_id, group_, line, zone, code, codegroup, timeevent, stateevent,date_key,priority,bitmask) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" % (eventId, event['objectId'], '1', 'V201', str(event['credoZoneCode']), str(event['contactIdCode']), '1', event['eventTime'].strftime('%Y-%m-%d %H:%M:%S') + '.000', '0', event['eventTime'].strftime('%Y%m%d'), '2', '0')
            glogger.debug(Logs.CREDODB, 'writeEvent: putTemp(%s)' % putTemp)
            cursor0.execute(putTemp)

        if event.has_key('isPhoto') and event['isPhoto'] == True:
            putEvent = "INSERT INTO %s (event_id, date_key, panel_id, group_, line, zone, code, codegroup, timeevent, stateevent, bitmask,metercount) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" % (dbtable1, eventId, event['eventTime'].strftime('%Y%m%d'), event['objectId'], '0', 'V201', str(event['credoZoneCode']), str(event['contactIdCode']), '1', event['eventTime'].strftime('%Y-%m-%d %H:%M:%S') + '.000', '0', '0', 'camera1')
        else:
            putEvent = "INSERT INTO %s (event_id, date_key, panel_id, group_, line, zone, code, codegroup, timeevent, stateevent, bitmask) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" % (dbtable1, eventId, event['eventTime'].strftime('%Y%m%d'), event['objectId'], '0', 'V201', str(event['credoZoneCode']), str(event['contactIdCode']), '1', event['eventTime'].strftime('%Y-%m-%d %H:%M:%S') + '.000', '0', '0')
        glogger.debug(Logs.CREDODB, 'writeEvent: putEvent(%s)' % putEvent)
        cursor1.execute(putEvent)

        updateCurrentId = "UPDATE archiveid SET current_id='%s'" % eventId
        cursor0.execute(updateCurrentId)
        glogger.debug(Logs.CREDODB, 'writeEvent: archiveid.current_id updated to "%s"' % eventId)

        if event.has_key('isPhoto') and event['isPhoto'] == True:
            dbtable2 = 'videoarchive' + datetime.datetime.today().strftime('%Y%m') + '01'

            url = 'http://dev.car-online.ru/api/v2?get=photo&skey=%s&id=%s&binary' % (event['skey'], event['photoId'])
            glogger.debug(Logs.CREDODB, 'writeEvent: photourl(%s)' % url)
            tmp = open('C:\\Magic\\tmp.image', 'wb')
            tmp.write(urllib2.urlopen(url).read())
            tmp.close()

            getPictureId    = 'SELECT MAX(pictureId) FROM %s' % dbtable2
            cursor2.execute(getPictureId)
            pictureId       = str(int(cursor2.fetchall()[0][0]) + 1)

            putPicture= "EXEC [pult4db_videoarchives].[dbo].[image_AddFile] @fileName='\\Magic\\tmp.image', @pictureId='%s', @objectId='%s', @videoZone='1', @Panel_Id='%s', @Result_Id='1', @event_id='%s', @TimeEvent='%s', @Date_Key='%s', @BitMask='0'" % (pictureId, event['objectId'], event['objectId'], eventId, event['eventTime'].strftime('%Y-%m-%d %H:%M:%S') + '.000', event['eventTime'].strftime('%Y%m%d'))
            glogger.debug(Logs.CREDODB, 'writeEvent: putPicture(%s)' % putPicture)
            cursor2.execute(putPicture)
            connection[2].commit()
        connection[1].commit()
        connection[0].commit()


    def writeEvents(self, processing, objects):
        totalwritten = 0
        for o in objects.values():
            try:
                written = 0
                failed = 0
                for e in o.events.values():
                    try:
                        if e.has_key('isPhoto') and e['isPhoto'] == True:
                            if e['contactIdCode'] != 'Z60':
                                self.writeEvent(e)
                            e['contactIdCode'] = 'Z60'
                            self.writeEvent(e)
                        else:
                            self.writeEvent(e)

                        written += 1
                        totalwritten += 1

                    except Exception, err:
                        glogger.warning(Logs.CREDODB, 'writeEvents: id(%s) eventid(%s) event(%s): %s' % (o.targetid, e['eventId'], str(e), str(err)))
                        if 'e_' + e['eventId'] in processing: processing.remove('e_' + e['eventId'])
                        if 'f_' + e['eventId'] in processing: processing.remove('f_' + e['eventId'])
                        failed += 1
                        continue

                glogger.info(Logs.CREDODB, 'writeEvent: id(%s) %d/%d written, %d errors' % (o.targetid, written, len(o.events), failed))
                o.events.clear()

            except Exception, err:
                glogger.error(Logs.CREDODB, 'writeEvents: id(%s): %s' % (o.targetid, str(err)))
                continue
        glogger.info(Logs.CREDODB, 'writeEvents: %d written' % (totalwritten))
